package com.etone.smartAudit.service;

import com.alibaba.fastjson.JSONObject;
import com.etone.smartAudit.domain.EsInfo;
import com.etone.smartAudit.domain.ServerCollect;
import com.etone.smartAudit.domain.ServerInfo;
import com.etone.smartAudit.dto.serverInfo.ServerInfoDTO;
import com.etone.smartAudit.enums.IapDataField;
import com.etone.smartAudit.error.CustomParameterizedException;
import com.etone.smartAudit.mapper.ServerInfoMapper;
import com.etone.smartAudit.mapstruct.ServerInfoMapStruct;
import com.etone.smartAudit.utils.DateUtil;
import com.etone.smartAudit.utils.ElasticSearchUtil;
import com.etone.smartAudit.utils.ElasticUtil;
import com.etone.smartAudit.vo.PagerVO;
import com.etone.smartAudit.vo.serverInfo.ServerInfoBaseVO;
import com.etone.smartAudit.vo.serverInfo.ServerInfoDetailVO;
import com.etone.smartAudit.vo.serverInfo.ServerInfoListVO;
import com.etone.smartAudit.vo.serverInfo.ServerInfoTotalVO;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * @author: 蒋学伟
 * @create: 2020-06-23 10:50:07
 * @Description ServerInfoService
 */
@Service
@AllArgsConstructor
@Slf4j
public class ServerInfoService {
    private ServerInfoMapper serverInfoMapper;

    private ElasticSearchUtil elasticSearchUtil;

    private ServerCollectService serverCollectService;

    private ServerDiskService serverDiskService;

    private ServerInfoMapStruct serverInfoMapStruct;

    /**
     * @param name
     * @param ip
     * @param id
     * @param field
     * @param manager
     * @param page
     * @param pageSize
     * @return
     */
    public PagerVO<ServerInfoListVO> list(String name, String ip, Integer id, IapDataField field, String manager, int page, int pageSize) {
        Page<ServerInfoListVO> serverInfoVOPage = PageHelper.startPage(page, pageSize).doSelectPage(() -> {
            serverInfoMapper.list(name, ip, id, field, manager);
        });
        return PagerVO.of(serverInfoVOPage);
    }

    /**
     * 详情
     *
     * @param id
     * @return
     */
    public ServerInfo get(Integer id) {
        ServerInfo serverInfo = serverInfoMapper.selectById(id);
        if (serverInfo == null) {
            throw new CustomParameterizedException("服务器不存在");
        }
        return serverInfo;
    }

    /**
     * 服务器基本信息
     *
     * @param itemId
     * @return
     */
    public List<ServerInfoBaseVO> listSimple(Integer itemId) {
        return serverInfoMapper.finListSimple(itemId);
    }

    /**
     * 统计服务器信息
     *
     * @return
     */
    public ServerInfoTotalVO serverInfoAnalysisTotal() {
        ServerInfoTotalVO serverInfoTotalVO = serverInfoMapper.sumInfo();

        return serverInfoTotalVO;
    }

    /**
     * 更新服务器资源信息
     */
    @Transactional(rollbackFor = Exception.class)
    public void doUpdateAllServerInfo() {
        //查询可更新的服务器
        List<ServerInfo> needCollectInfoServer = serverInfoMapper.findNeedCollectInfoServer();
        if (!CollectionUtils.isEmpty(needCollectInfoServer)) {
            log.info("查询到{}个可更新服务器", needCollectInfoServer.size());
            for (ServerInfo serverInfo : needCollectInfoServer) {
                EsInfo esInfo = serverInfo.getEsInfo();
                if (esInfo != null && !StringUtils.isEmpty(esInfo.getIndex()) && !CollectionUtils.isEmpty(esInfo.getTargets()) && !StringUtils.isEmpty(serverInfo.getDeviceName()) && !StringUtils.isEmpty(serverInfo.getIp())) {
                    List<String> targets = esInfo.getTargets();

                    //服务器记录
                    ServerCollect serverCollect = new ServerCollect();

                    //遍历信息采集
                    for (String target : targets) {
                        switch (target.toLowerCase()) {
                            case "memory":
                                collectMemory(serverInfo, serverCollect);
                                break;
                            case "cpu":
                                collectCpu(serverInfo, serverCollect);
                                break;
                            case "fsstat":
                                collectFsstat(serverInfo, serverCollect);
                                break;
                            case "filesystem":
                                serverDiskService.collectDisk(serverInfo);
                                break;
                            default:

                        }
                    }
                    serverInfoMapper.updateEsInfo(serverInfo);
                    serverCollect.setServerInfoId(serverInfo.getId());
                    serverCollect.setDtCreateTime(new Date());
                    serverCollectService.save(serverCollect);
                }

            }
        }
    }

    /**
     * 搜集服务器内存信息
     *
     * @param serverInfo
     * @param serverCollect
     */
    public void collectMemory(ServerInfo serverInfo, ServerCollect serverCollect) {
        String target = "memory";
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder
                .must(QueryBuilders.matchQuery("metricset.name", target).operator(Operator.AND))
                .must(QueryBuilders.matchQuery("fields.ip", serverInfo.getIp()).operator(Operator.AND));
        SearchSourceBuilder searchSourceBuilder = ElasticUtil.initSearchSourceBuilder(boolQueryBuilder, 0, 1, 60);
        searchSourceBuilder.fetchSource(new String[]{"system", "host", "@timestamp"}, null);
        searchSourceBuilder.sort("@timestamp", SortOrder.DESC);
        log.info("查询{}条件:{}", target, searchSourceBuilder.toString());
        List<JSONObject> data = elasticSearchUtil.search(serverInfo.getEsInfo().getIndex(), searchSourceBuilder, JSONObject.class);
        if (!CollectionUtils.isEmpty(data)) {
            JSONObject dataInfoJson = data.get(0);
            String utcTime = dataInfoJson.getString("@timestamp");
            Date date = DateUtil.parseUTC2Date(utcTime);
            Date now = new Date();
            if ((now.getTime() - date.getTime()) <= 10 * 60 * 1000) {
                log.info("查询{}服务器{}:{}", serverInfo.getName(), target, dataInfoJson.toJSONString());
                //内存大小，单位B
                Double memoryTotal = dataInfoJson.getJSONObject("system").getJSONObject("memory").getDouble("total");
                serverInfo.setMemory(memoryTotal / Math.pow(1024, 3));

                //剩余内存大小,单位B
                Double memoryFree = dataInfoJson.getJSONObject("system").getJSONObject("memory").getJSONObject("actual").getDouble("free");
                Double memoryUserRate = (memoryTotal - memoryFree) / memoryTotal * 100;
                serverInfo.setMemoryUsed(memoryUserRate);

                //内存采集记录
                serverCollect.setMemoryInfo(dataInfoJson.toJSONString());
                serverCollect.setMemory(memoryTotal / Math.pow(1024, 3));
                serverCollect.setMemoryUsed((memoryTotal - memoryFree) / Math.pow(1024, 3));
                serverCollect.setMemoryUsedRate(memoryUserRate);
            }
        } else {
            log.info("未查询到{}服务器{},设备名称:{}", serverInfo.getName(), target, serverInfo.getDeviceName());
        }
    }

    /**
     * 搜集服务器cpu信息
     *
     * @param serverInfo
     * @param serverCollect
     */
    public void collectCpu(ServerInfo serverInfo, ServerCollect serverCollect) {
        String target = "cpu";
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder
                .must(QueryBuilders.matchQuery("metricset.name", target).operator(Operator.AND))
                .must(QueryBuilders.matchQuery("fields.ip", serverInfo.getIp()).operator(Operator.AND));
        SearchSourceBuilder searchSourceBuilder = ElasticUtil.initSearchSourceBuilder(boolQueryBuilder, 0, 1, 60);
        searchSourceBuilder.fetchSource(new String[]{"system", "host", "@timestamp"}, null);
        searchSourceBuilder.sort("@timestamp", SortOrder.DESC);
        log.info("查询{}条件:{}", target, searchSourceBuilder.toString());
        List<JSONObject> data = elasticSearchUtil.search(serverInfo.getEsInfo().getIndex(), searchSourceBuilder, JSONObject.class);
        if (!CollectionUtils.isEmpty(data)) {
            JSONObject dataInfoJson = data.get(0);
            String utcTime = dataInfoJson.getString("@timestamp");
            Date date = DateUtil.parseUTC2Date(utcTime);
            Date now = new Date();
            if ((now.getTime() - date.getTime()) <= 10 * 60 * 1000) {
                log.info("查询{}服务器{}:{}", serverInfo.getName(), target, dataInfoJson.toJSONString());
                //cpu核数
                Integer cpuCnt = dataInfoJson.getJSONObject("system").getJSONObject("cpu").getInteger("cores");
                serverInfo.setCpuCnt(cpuCnt);

                if (cpuCnt > 0) {
                    //System cpu使用率
                    Double systemCpuUsedRate = dataInfoJson.getJSONObject("system").getJSONObject("cpu").getJSONObject("system").getDouble("pct");
                    //User cpu使用率
                    Double userCpuUsedRate = dataInfoJson.getJSONObject("system").getJSONObject("cpu").getJSONObject("user").getDouble("pct");
                    //真实 cpu用率
                    double realCpuRate = (systemCpuUsedRate + userCpuUsedRate) / cpuCnt * 100d;
                    serverInfo.setCpuUsedRate(realCpuRate);

                    //cup采集记录
                    serverCollect.setCpuInfo(dataInfoJson.toJSONString());
                    serverCollect.setCpuUsedRate(realCpuRate);
                }
            }
        } else {
            log.info("未查询到{}服务器{},设备名称:{}", serverInfo.getName(), target, serverInfo.getDeviceName());
        }
    }

    /**
     * 搜集磁盘总空间信息
     *
     * @param serverInfo
     * @param serverCollect
     */
    public void collectFsstat(ServerInfo serverInfo, ServerCollect serverCollect) {
        String target = "fsstat";
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder
                .must(QueryBuilders.matchQuery("metricset.name", target).operator(Operator.AND))
                .must(QueryBuilders.matchQuery("fields.ip", serverInfo.getIp()).operator(Operator.AND));
        SearchSourceBuilder searchSourceBuilder = ElasticUtil.initSearchSourceBuilder(boolQueryBuilder, 0, 1, 60);
        searchSourceBuilder.fetchSource(new String[]{"system", "host", "@timestamp"}, null);
        searchSourceBuilder.sort("@timestamp", SortOrder.DESC);
        log.info("查询{}条件:{}", target, searchSourceBuilder.toString());
        List<JSONObject> data = elasticSearchUtil.search(serverInfo.getEsInfo().getIndex(), searchSourceBuilder, JSONObject.class);
        if (!CollectionUtils.isEmpty(data)) {
            JSONObject dataInfoJson = data.get(0);
            String utcTime = dataInfoJson.getString("@timestamp");
            Date date = DateUtil.parseUTC2Date(utcTime);
            Date now = new Date();
            if ((now.getTime() - date.getTime()) <= 10 * 60 * 1000) {
                log.info("查询{}服务器{}:{}", serverInfo.getName(), target, dataInfoJson.toJSONString());
                //磁盘总空间，单位B
                Double spaceTotal = dataInfoJson.getJSONObject("system").getJSONObject("fsstat").getJSONObject("total_size").getDouble("total");
                serverInfo.setSpace(spaceTotal / Math.pow(1024, 3));
                //磁盘已使用空间，单位B
                Double spaceUsed = dataInfoJson.getJSONObject("system").getJSONObject("fsstat").getJSONObject("total_size").getDouble("used");
                serverInfo.setSpaceUsed(spaceUsed / Math.pow(1024, 3));

                //磁盘采集记录
                serverCollect.setFsstatInfo(dataInfoJson.toJSONString());
                serverCollect.setSpace(spaceTotal / Math.pow(1024, 3));
                serverCollect.setSpaceUsed(spaceUsed / Math.pow(1024, 3));
                serverCollect.setSpaceUsedRate(spaceUsed / spaceTotal * 100);
            }
        } else {
            log.info("未查询到{}服务器{},设备名称:{}", serverInfo.getName(), target, serverInfo.getDeviceName());
        }
    }

    /**
     * 清理服务器历史数据
     *
     * @param startTime
     * @param endTime
     */
    public void doCleanServerInfo(Date startTime, Date endTime) {
        int count = serverCollectService.cleanServerInfo(startTime, endTime);
        log.info("总共清理{}条数据", count);
    }

    /**
     * 服务器详情
     *
     * @param id
     * @return
     */
    public ServerInfoDetailVO detail(Integer id) {
        ServerInfo serverInfo = get(id);
        ServerInfoDetailVO serverInfoDetailVO = serverInfoMapStruct.getServerInfoDetailVO(serverInfo);
        serverInfoDetailVO.setIsMonitor(serverInfo.getEsInfo() != null);
        return serverInfoDetailVO;
    }

    /**
     * 编辑|新增
     *
     * @param id
     * @param serverInfoDTO
     */
    public void save(Integer id, ServerInfoDTO serverInfoDTO) {
        ServerInfo serverInfo;
        if (id == null) {
            serverInfo = ServerInfo.builder().id(id).build();
        } else {
            serverInfo = get(id);
        }
        serverInfoMapStruct.copyToServerInfo(serverInfo, serverInfoDTO);
        if (serverInfoDTO.getIsMonitor() != null && serverInfoDTO.getIsMonitor()) {
            //{"index":"metricbeat-7.6.2*","targets":["memory","cpu","fsstat","filesystem"]}
            serverInfo.setEsInfo(EsInfo.builder().index("metricbeat*").targets(Arrays.asList("memory", "cpu", "fsstat", "filesystem")).build());
        }
        if (id != null) {
            serverInfoMapper.updateById(serverInfo);
        } else {
            serverInfoMapper.insert(serverInfo);
        }

    }

    /**
     * 删除服务器
     *
     * @param id
     */
    public void delete(Integer id) {
        serverInfoMapper.deleteById(id);
    }
}
